import matplotlib.pyplot as plt

from keras import backend as k
from keras.applications import VGG16
import numpy as np
import tensorflow as tf
'''
可视化卷积神经网络的过滤器
'''

# 运行程序出错，百度后添加
tf.compat.v1.disable_eager_execution()
# 为过滤器的可视化定义损失张量
model = VGG16(weights='imagenet',
              include_top=False)
layer_name = 'block3_conv1'
filter_index = 0
layer_output = model.get_layer(layer_name).output
loss = k.mean(layer_output[:, :, :, filter_index])



# 获取损失相对于输入的梯度
grads = k.gradients(loss, model.input)[0]
# 梯度标准化
grads /= (k.sqrt(k.mean(k.square(grads))) + 1e-5)
# 给定numpy输入值,得到numpy输出值
iterate = k.function([model.input], [loss, grads])
loss_value, grads_value = iterate([np.zeros((1, 150, 150, 3))])
# 通过随机梯度下降让损失最大化
input_img_data = np.random.random((1, 150, 150, 3)) * 20 + 128.
# 每次梯度更新的步长
step = 1.
for i in range(40):
    # 运行40次梯度上升
    loss_value, grads_value = iterate([input_img_data])
    # 沿着让损失最大化的方向调节输入图像
    input_img_data += grads_value * step

'''
将张量转化为有效图像的使用函数
'''
def deprocess_image(x):
    # 对张量做标准化，使其均值为0，标准差为0.1
    x -= x.mean()
    x /= (x.std() + 1e-5)
    x *= 0.1
    # 将x裁切（clip）到[0,1]区间
    x += 0.5
    x = np.clip(x, 0, 1)
    # 将x转换为RGB数组
    x *= 255
    x = np.clip(x, 0, 255).astype('uint8')
    return x

'''
生成过滤器可视化的函数
'''
def generate_pattern(layer_name, filter_index,size=150):
    # 构建一个损失函数，将该层第n个过滤器的激活最大化
    layer_output = model.get_layer(layer_name).output
    loss = k.mean(layer_output[:, :, :, filter_index])
    #计算这个损失相对于输入图像的梯度
    grads = k.gradients(loss, model.input)[0]
    # 标准化技巧：将梯度标准化
    grads /= (k.sqrt(k.mean(k.square(grads))) + 1e-5)
    # 返回给定输入图像的损失和梯度
    iterate = k.function([model.input], [loss, grads])
    # 从带有噪声的灰度图开始
    input_img_data = np.random.random((1, size, size, 3) ) * 20 + 128.

    # 运行40次梯度上升
    step = 1.
    for i in range(40):
        loss_value, grads_value = iterate([input_img_data])
        input_img_data += grads_value * step

    img = input_img_data[0]
    return deprocess_image(img)

# 第0个过滤器图（波尔卡点图）
# plt.imshow(generate_pattern('block3_conv1', 0))
# plt.show()
"""
生成某一层中所有过滤器响应模式组成的网络
"""

layer_name = 'block1_conv1'
size = 64
margin = 5
results = np.zeros((8 * size + 7 * margin,8 * size + 7 * margin, 3))
for i in range(8):
    for j in range(8):
        filter_img = generate_pattern(layer_name, i + (j * 8), size=size)
        horizontal_start = i * size + i * margin
        horizontal_end = horizontal_start + size
        vertical_start = j * size + j * margin
        vertical_end = vertical_start + size
        results[horizontal_start: horizontal_end,
        vertical_start: vertical_end, :] = filter_img

plt.figure(figsize=(20, 20))

plt.imshow(results.astype('uint8'))
plt.show()


